package com.tqyan.service.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.tqyan.dal.mapper.ChatContentMapper;
import com.tqyan.dal.mapper.ChatMapper;
import com.tqyan.dal.mapper.UserSettingMapper;
import com.tqyan.model.Result;
import com.tqyan.model.controller.request.OpenAIChatRequest;
import com.tqyan.model.controller.request.OpenAIDrawRequest;
import com.tqyan.model.controller.response.ChatContentResp;
import com.tqyan.model.controller.response.OpenAIChatResp;
import com.tqyan.model.controller.response.OpenAIDrawResp;
import com.tqyan.model.dto.ChatContentDTO;
import com.tqyan.model.dto.ChatDTO;
import com.tqyan.model.dto.UserDTO;
import com.tqyan.model.dto.UserSettingDTO;
import com.tqyan.model.enums.*;
import com.tqyan.model.exp.DefaultException;
import com.tqyan.service.service.AccountService;
import com.tqyan.service.service.ChatDrawService;
import com.tqyan.service.service.UserService;
import com.tqyan.service.transfer.FileService;
import com.tqyan.service.transfer.LimitService;
import com.tqyan.service.transfer.OpenAIService;
import com.tqyan.util.DateUtil;
import com.tqyan.util.DisableWordUtil;
import com.tqyan.util.GuidUtil;
import com.tqyan.util.SnowflakeUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.Executor;

/**
 * 会话-绘画服务
 * <p> <功能详细描述> </p>
 *
 * @author 陈晨
 * @version 1.0
 * @date 2023/4/12
 */
@Service
@Slf4j
public class ChatDrawServiceImpl implements ChatDrawService {

    @Autowired
    private UserSettingMapper userSettingMapper;
    @Autowired
    private ChatMapper chatMapper;
    @Autowired
    private ChatContentMapper chatContentMapper;

    @Resource(name = "chatExecutor")
    private Executor chatExecutor;
    @Autowired
    private UserService userService;
    @Autowired
    private AccountService accountService;
    @Autowired
    private OpenAIService openAIService;
    @Autowired
    private FileService fileService;
    @Autowired
    private LimitService limitService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ChatContentResp draw(String openId, String content) {
        if (StringUtils.isBlank(content)) {
            throw new DefaultException("内容可不为空");
        }
        // 描述最多350个字符
        if (content.length() > 350) {
            throw new DefaultException("绘画描述最多350个字符");
        }
        // 禁用词检查
        if (DisableWordUtil.containsDisable(content)) {
            throw new DefaultException("请文明用语");
        }
        // 用户不存在会话, 则创建
        UserDTO user = userService.getUserInfo(openId);
//        // 用户存在未完成的答案, 禁止继续提问
//        if (chatContentMapper.countIncomplete(user.getId()) > 0) {
//            throw new DefaultException("小燕子还有没完成的作品, 请耐心等待哦");
//        }
        // 获取用户设置, TODO 后期还原回会话设置
        UserSettingDTO userSetting = userSettingMapper.queryByUser(user.getId());
        // 是否欠费
        if (accountService.isArrears(user.getId(), 50)) {
            throw new DefaultException("501", "余额不足");
        }
        // 获取会话
        ChatDTO chat = chatMapper.queryByDraw(user.getId());
        if (null == chat) {
            throw new DefaultException("会话未创建");
        }
        // 会话锁
        boolean isLock = limitService.lockChat(openId, chat.getId());
        if (!isLock) {
            throw new DefaultException("小燕子正在努力绘画, 请耐心等待哦");
        }
        // 创建绘画需求
        ChatContentDTO question = new ChatContentDTO();
        question.setId(SnowflakeUtils.nextId());
        question.setUserId(user.getId());
        question.setChatId(chat.getId());
        question.setModel(userSetting.getModel());
        question.setTemperature(ChatCreativeAbilityEnum.get(userSetting.getCreativeAbility()).getTemperature());
        question.setRole(GPTRoleEnum.USER.getValue());
        question.setContent(content);
        question.setType(ChatContentTypeEnum.PICTURE.getValue());
        question.setStatus(ChatContentStatusEnum.COMPLETED.getValue());
        boolean result = chatContentMapper.save(question) > 0;
        if (!result) {
            throw new DefaultException("问题入库失败");
        }
//        log.info("[绘画] openId={}, chatId={}, questionId={}, 问题入库完成", openId, chat.getId(), question.getId());
        // 创建绘画结果
        ChatContentDTO answer = new ChatContentDTO();
        answer.setId(SnowflakeUtils.nextId());
        answer.setUserId(0L);
        answer.setChatId(question.getChatId());
        answer.setModel(userSetting.getModel());
        answer.setTemperature(ChatCreativeAbilityEnum.get(userSetting.getCreativeAbility()).getTemperature());
        answer.setRole(GPTRoleEnum.ASSISTANT.getValue());
        answer.setContent("小燕子正在努力绘制中");
        answer.setType(ChatContentTypeEnum.PICTURE.getValue());
        answer.setStatus(ChatContentStatusEnum.INCOMPLETE.getValue());
        answer.setQuestionId(question.getId());
        result = chatContentMapper.save(answer) > 0;
        if (!result) {
            throw new DefaultException("答案入库失败");
        }
//        log.info("[绘画] openId={}, chatId={}, answer={}, 答案入库完成", openId, chat.getId(), answer.getId());

        // 请求绘画
        chatExecutor.execute(() -> {
            try {
                this.requestDraw(user, question, answer);
            } catch (Exception e) {
                ChatContentDTO updateChatContent = new ChatContentDTO();
                updateChatContent.setId(answer.getId());
                updateChatContent.setChatId(answer.getChatId());
//                updateChatContent.setContent("小燕子太累了, 让它休息一会吧");
                updateChatContent.setContent("抱歉，远程服务器负载过高，暂时无法绘画，您可以稍后尝试。");
                updateChatContent.setStatus(ChatContentStatusEnum.EXCEPTION.getValue());
                chatContentMapper.update(updateChatContent);
                log.error("[绘画] userId={}, chatId={}, questionId={}, answerId={}, GPT请求异常: {}"
                        , user.getId(), question.getChatId(), question.getId(), answer.getId()
                        , e.getMessage(), e);
            } finally {
                limitService.unlockChat(user.getOpenId(), question.getChatId());
            }
        });
//        this.requestDraw(user, question, answer);
//        answer = chatContentMapper.queryById(answer.getId());

        // 响应
        ChatContentResp resp = new ChatContentResp();
        resp.setUserId("0");
        resp.setChatId(answer.getChatId() + "");
        resp.setChatContentId(answer.getId() + "");
        resp.setContent(answer.getContent());
        resp.setType(answer.getType());
        resp.setStatus(answer.getStatus());
        return resp;
    }

    /**
     * @description 请求绘画
     * <p>
     *     AccessKey ID：LTAI5t74D3XZt8TgCXbvfMCT
     *     AccessKey Secret：URsOODRnN8YOi5PA8WPkJKklh07fKw
     * </p>
     *
     * @author 陈晨
     * @date 2023/4/13 19:43
     */
    private void requestDraw(UserDTO user, ChatContentDTO question, ChatContentDTO answer) {
//        // 需求翻译为英文
//        long startTime = System.currentTimeMillis();
//        OpenAIChatRequest chatRequest = new OpenAIChatRequest();
//        chatRequest.setModel(GPTModelEnum.GPT_3_5.getValue());
//        chatRequest.setTemperature(1D);
//        chatRequest.addMessage(GPTRoleEnum.USER.getValue(), "翻译成英语：" + question.getContent());
//        OpenAIChatResp chatResp =  openAIService.requestChatComplete(chatRequest);
//        long endTime = System.currentTimeMillis();
//        log.info("[绘画] userId={}, chatId={}, question={}, 请求GPT翻译, 耗时: {}ms"
//                , user.getId(), question.getChatId(), question.getContent(), endTime - startTime);
        // 请求GPT
        GPTSizeEnum size = GPTSizeEnum.SIZE_1024;
        long startTime = System.currentTimeMillis();
        OpenAIDrawRequest drawRequest = new OpenAIDrawRequest();
//        drawRequest.setPrompt(chatResp.getChoices().get(0).getMessage().getContent());
        drawRequest.setPrompt(question.getContent());
        drawRequest.setN(1);
        drawRequest.setSize(size.getValue());
        OpenAIDrawResp drawResp = openAIService.requestDraw(drawRequest);
        long endTime = System.currentTimeMillis();
        String imageUrl = drawResp.getData().get(0).getUrl();
        log.info("[绘画] userId={}, chatId={}, imageUrl={}, 请求GPT绘画, 耗时: {}ms"
                , user.getId(), question.getChatId(), imageUrl, endTime - startTime);

        // 下载图片, 图片上传OSS
        String fileName = GuidUtil.getUUID() + ".png";
        fileService.downloadAndUploadAliyunOSS(imageUrl, FileService.FOLDER_DRAW, fileName);
        log.info("[绘画] userId={}, chatId={}, questionId={}, answerId={}, 下载图片, 图片上传OSS"
                , user.getId(), question.getChatId(), question.getId(), answer.getId());

        // 更新答案已完成
        ChatContentDTO updateChatContent = new ChatContentDTO();
        updateChatContent.setId(answer.getId());
        updateChatContent.setChatId(answer.getChatId());
        updateChatContent.setContent(fileName);
        updateChatContent.setGptResponseTime(DateUtil.now(startTime));
        updateChatContent.setGptCompleteTime(DateUtil.now(endTime));
        updateChatContent.setStatus(ChatContentStatusEnum.COMPLETED.getValue());
        boolean result = chatContentMapper.update(updateChatContent) > 0;
        log.info("[绘画] userId={}, chatId={}, questionId={}, answerId={}, 绘画完成: {}"
                , user.getId(), question.getChatId(), question.getId(), answer.getId(), result);

        // 费用扣除, 添加账单
        JSONObject explain = new JSONObject();
        explain.put("info", "绘画");
        explain.put("question", question.getId());
        explain.put("answer", answer.getId());
        explain.put("size", size.getValue());
        result = accountService.consumption(user.getId(), size.getPrice(), explain.toJSONString());
        log.info("[绘画] userId={}, chatId={}, questionId={}, answerId={}, size={}, 费用扣除, 添加账单: {}"
                , user.getId(), question.getChatId(), question.getId(), answer.getId(), size, result);
    }

}


